/*
 * Copyright (C) 2019. Uber Technologies
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.lzy.okgo.harmony.autodispose;


import com.uber.autodispose.OutsideScopeException;
import com.uber.autodispose.lifecycle.CorrespondingEventsFunction;
import com.uber.autodispose.lifecycle.LifecycleEndedException;
import com.uber.autodispose.lifecycle.LifecycleScopeProvider;
import com.uber.autodispose.lifecycle.LifecycleScopes;
import io.reactivex.CompletableSource;
import io.reactivex.Observable;
import io.reactivex.annotations.NonNull;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.ability.Lifecycle;
import ohos.aafwk.ability.fraction.Fraction;

/**
 * <p>
 *
 * <pre><code>
 *   AutoDispose.autoDisposable(ohosLifecycleScopeProvider.from(lifecycleOwner))
 * </code></pre>
 */
public final class HarmonyLifecycleScopeProvider
        implements LifecycleScopeProvider<Lifecycle.Event> {

    private static final CorrespondingEventsFunction<Lifecycle.Event> DEFAULT_CORRESPONDING_EVENTS =
            lastEvent -> {
                switch (lastEvent) {
                    case ON_START:
                        return Lifecycle.Event.ON_STOP;
//          case ON_FOREGROUND:
//            return Lifecycle.Event.ON_INACTIVE;
//          case ON_INACTIVE:
//            return Lifecycle.Event.ON_ACTIVE;
                    case ON_ACTIVE:
                        return Lifecycle.Event.ON_INACTIVE;
                    case ON_INACTIVE:
                        return Lifecycle.Event.ON_BACKGROUND;
                    case ON_BACKGROUND:
                    case ON_STOP:
                    default:
                        throw new LifecycleEndedException("Lifecycle has ended! Last event was " + lastEvent);
                }
            };
    private final CorrespondingEventsFunction<Lifecycle.Event> boundaryResolver;

    /**
     * Creates a {@link HarmonyLifecycleScopeProvider} for Ohos LifecycleOwners.
     *
     * @param owner the owner to scope for.
     * @return a {@link HarmonyLifecycleScopeProvider} against this owner.
     */
    public static HarmonyLifecycleScopeProvider from(Ability owner) {
        return from(owner.getLifecycle());
    } /**
     * Creates a {@link HarmonyLifecycleScopeProvider} for ohos LifecycleOwners.
     *
     * @param owner the owner to scope for.
     * @return a {@link HarmonyLifecycleScopeProvider} against this owner.
     */

    /**
     * Creates a {@link HarmonyLifecycleScopeProvider} for ohos LifecycleOwners.
     *
     * @param owner the owner to scope for.
     * @return a {@link HarmonyLifecycleScopeProvider} against this owner.
     */
    public static HarmonyLifecycleScopeProvider from(Fraction owner) {
        return from(owner.getLifecycle());
    }

    /**
     * Creates a {@link HarmonyLifecycleScopeProvider} for ohos LifecycleOwners.
     *
     * @param owner the owner to scope for.
     * @return a {@link HarmonyLifecycleScopeProvider} against this owner.
     */
    public static HarmonyLifecycleScopeProvider from(AbilitySlice owner) {
        return from(owner.getLifecycle());
    }

    /**
     * Creates a {@link HarmonyLifecycleScopeProvider} for ohos LifecycleOwners.
     *
     * @param owner      the owner to scope for.
     * @param untilEvent the event until the scope is valid.
     * @return a {@link HarmonyLifecycleScopeProvider} against this owner.
     */
    public static HarmonyLifecycleScopeProvider from(
            Ability owner, Lifecycle.Event untilEvent) {
        return from(owner.getLifecycle(), untilEvent);
    }

    /**
     * Creates a {@link HarmonyLifecycleScopeProvider} for ohos LifecycleOwners.
     *
     * @param owner      the owner to scope for.
     * @param untilEvent the event until the scope is valid.
     * @return a {@link HarmonyLifecycleScopeProvider} against this owner.
     */
    public static HarmonyLifecycleScopeProvider from(
            AbilitySlice owner, Lifecycle.Event untilEvent) {
        return from(owner.getLifecycle(), untilEvent);
    }

    /**
     * Creates a {@link HarmonyLifecycleScopeProvider} for ohos LifecycleOwners.
     *
     * @param owner      the owner to scope for.
     * @param untilEvent the event until the scope is valid.
     * @return a {@link HarmonyLifecycleScopeProvider} against this owner.
     */
    public static HarmonyLifecycleScopeProvider from(
            Fraction owner, Lifecycle.Event untilEvent) {
        return from(owner.getLifecycle(), untilEvent);
    }

    /**
     * Creates a {@link HarmonyLifecycleScopeProvider} for ohos Lifecycles.
     *
     * @param lifecycle the lifecycle to scope for.
     * @return a {@link HarmonyLifecycleScopeProvider} against this lifecycle.
     */
    public static HarmonyLifecycleScopeProvider from(Lifecycle lifecycle) {
        return from(lifecycle, DEFAULT_CORRESPONDING_EVENTS);
    }

    /**
     * Creates a {@link HarmonyLifecycleScopeProvider} for ohos Lifecycles.
     *
     * @param lifecycle  the lifecycle to scope for.
     * @param untilEvent the event until the scope is valid.
     * @return a {@link HarmonyLifecycleScopeProvider} against this lifecycle.
     */
    public static HarmonyLifecycleScopeProvider from(
            Lifecycle lifecycle, Lifecycle.Event untilEvent) {
        return from(lifecycle, new UntilEventFunction(untilEvent));
    }

    /**
     * Creates a {@link HarmonyLifecycleScopeProvider} for Ohos Lifecycles.
     *
     * @param owner            the owner to scope for.
     * @param boundaryResolver function that resolves the event boundary.
     * @return a {@link HarmonyLifecycleScopeProvider} against this owner.
     */
    public static HarmonyLifecycleScopeProvider from(
            Ability owner, CorrespondingEventsFunction<Lifecycle.Event> boundaryResolver) {
        return from(owner.getLifecycle(), boundaryResolver);
    }

    /**
     * Creates a {@link HarmonyLifecycleScopeProvider} for Ohos Lifecycles.
     *
     * @param lifecycle        the lifecycle to scope for.
     * @param boundaryResolver function that resolves the event boundary.
     * @return a {@link HarmonyLifecycleScopeProvider} against this lifecycle.
     */
    public static HarmonyLifecycleScopeProvider from(
            Lifecycle lifecycle, CorrespondingEventsFunction<Lifecycle.Event> boundaryResolver) {
        return new HarmonyLifecycleScopeProvider(lifecycle, boundaryResolver);
    }

    private final LifecycleEventsObservable lifecycleObservable;

    private HarmonyLifecycleScopeProvider(
            Lifecycle lifecycle, CorrespondingEventsFunction<Lifecycle.Event> boundaryResolver) {
        this.lifecycleObservable = new LifecycleEventsObservable(lifecycle);
        this.boundaryResolver = boundaryResolver;
    }

    @Override
    public Observable<Lifecycle.Event> lifecycle() {
        return lifecycleObservable;
    }

    @Override
    public CorrespondingEventsFunction<Lifecycle.Event> correspondingEvents() {
        return boundaryResolver;
    }

    @Override
    public Lifecycle.Event peekLifecycle() {
        lifecycleObservable.backfillEvents();
        return lifecycleObservable.getValue();
    }

    @Override
    public CompletableSource requestScope() {
        return LifecycleScopes.resolveScopeFromLifecycle(this);
    }

    private static class UntilEventFunction implements CorrespondingEventsFunction<Lifecycle.Event> {
        private final Lifecycle.Event untilEvent;

        UntilEventFunction(Lifecycle.Event untilEvent) {
            this.untilEvent = untilEvent;
        }

        @Override
        public Lifecycle.Event apply(@NonNull Lifecycle.Event event) throws OutsideScopeException {
            return untilEvent;
        }
    }
}
